package com.bcbim.api_web.sys.interceptor;

import com.alibaba.fastjson.JSON;
import com.bcbim.api_web.sys.redis.MyRedisService;
import com.bcbim.authentication_interface.user.dto.LoginUserDTO;
import com.bcbim.authentication_interface.util.AuthUtil;
import com.bcbim.common.response.vo.ReturnMsg;
import com.bcbim.common.sys.Constant;
import com.bcbim.common.sys.ReturnStatus;
import com.bcbim.common.util.AESUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @Description: 鉴权拦截器
 * @Author haoshuai
 * @Date 2020/11/5
 * @Version V1.0
 **/
@Slf4j
@Component
public class AuthenticationInterceptor implements HandlerInterceptor {

    /**
     * cookie 中 token key
     */
    private static final String TOKEN_NAME = "token";

    @Autowired
    private MyRedisService myRedisService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        // options 请求直接放过
        if(request.getMethod().toLowerCase().equals("options")) {
            return Boolean.TRUE;
        }

        // 若为登录请求, 跳过token检验 || 若为下载文件 放过
        String url = request.getRequestURI();
        if (url.contains(Constant.USERLOGIN_METHOD_NAME) || url.contains(Constant.RESOURCES_PATH)) {
            return Boolean.TRUE;
        }

        // 获取token
        String token = StringUtils.isBlank(request.getHeader(TOKEN_NAME)) ? request.getParameter(TOKEN_NAME) : request.getHeader(TOKEN_NAME);

        if (StringUtils.isEmpty(token)) {
            responseMsg(response,
                    HttpServletResponse.SC_UNAUTHORIZED,
                    ReturnMsg.consFail(ReturnStatus.UNAUTHORIZED.getCode(), Constant.UNAUTHORIZED_MSG));
            return Boolean.FALSE;
        }

        /*
        将token进行解密, 解密后再进行校验
        框架中生成token采用GuidUtil, 所以利用验证Guid逻辑进行校验
         */
        String decryptToken = AESUtil.decrypt(token);
        if (StringUtils.isEmpty(decryptToken) || decryptToken.length() != Constant.ID_LENGTH) {
            responseMsg(response,
                    HttpServletResponse.SC_UNAUTHORIZED,
                    ReturnMsg.consFail(ReturnStatus.UNAUTHORIZED.getCode(), Constant.UNAUTHORIZED_MSG));
            return Boolean.FALSE;
        }

        /*
        校验通过后, 查询token令牌是否存在
        不存在: 返回未授权提示
        存在: 刷新时间
         */
        boolean b = myRedisService.hasKey(token);
        if (!b) {
            responseMsg(response,
                    HttpServletResponse.SC_UNAUTHORIZED,
                    ReturnMsg.consFail(ReturnStatus.UNAUTHORIZED.getCode(), Constant.UNAUTHORIZED_MSG));
            return Boolean.FALSE;
        }

        // 存在: 刷新时间, LoginUser 对象写入ThreadLocal
        Object o = myRedisService.get(token);
        myRedisService.set(token, o, Constant.PERMISSION_EXPIRATION_TIME);
        AuthUtil.setUser(JSON.toJavaObject((JSON) o, LoginUserDTO.class));
        return Boolean.TRUE;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    }

    /**
     * @Description: 返回客户端信息
     * @Param: response HttpServletResponse对象
     * @Param: responseStatus 系统返回码
     * @Param: returnMsg 返回对象
     * @Author: haoshuai
     * @Date: 2020/11/5
     */
    private void responseMsg(HttpServletResponse response, int responseStatus, ReturnMsg returnMsg) throws IOException {
        response.setStatus(responseStatus);
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        PrintWriter out;
        out = response.getWriter();
        out.write(JSON.toJSON(returnMsg).toString());
        out.flush();
        out.close();
    }

}
